// Copyright 2021-2025 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { suite, test } from "node:test";
import * as assert from "node:assert";
import { createTestPluginAndRun } from "./helpers.js";

void suite("GeneratedFile.preamble", () => {
  void test("contains plugin name and version", async () => {
    const lines = await testGenerate({
      proto: `syntax="proto3";`,
      name: "pi-plugin",
      version: "v3.14159",
    });
    assert.ok(lines.includes("// @generated by pi-plugin v3.14159"));
  });

  void test("contains plugin options", async () => {
    const lines = await testGenerate({
      proto: `syntax="proto3";`,
      parameter: "foo=bar,baz",
    });
    assert.ok(
      lines.includes(`// @generated by test v1 with parameter "foo=bar,baz"`),
    );
  });

  void test("elides rewrite_imports plugin option", async () => {
    const lines = await testGenerate({
      proto: `syntax="proto3";`,
      parameter:
        "foo,rewrite_imports=./test/*_pb.js:@buf/test,rewrite_imports=./test/*_web.js:@buf/web,bar",
    });
    assert.ok(
      lines.includes(`// @generated by test v1 with parameter "foo,bar"`),
    );
  });

  void test("contains eslint-disable annotation", async () => {
    const lines = await testGenerate({
      proto: `syntax="proto3";`,
    });
    assert.ok(lines.includes("/* eslint-disable */"));
  });

  void test("does not contain ts-nocheck annotation by default", async () => {
    const lines = await testGenerate({
      proto: `syntax="proto3";`,
    });
    assert.ok(!lines.includes("// @ts-nocheck"));
  });

  void test("contains ts-nocheck annotation when opted in", async () => {
    const lines = await testGenerate({
      proto: `syntax="proto3";`,
      parameter: "ts_nocheck=true",
    });
    assert.ok(lines.includes("// @ts-nocheck"));
  });

  void test("contains source file info for proto3", async () => {
    const lines = await testGenerate({
      proto: {
        "foo/bar.proto": `syntax="proto3";`,
      },
    });
    assert.ok(
      lines.includes("// @generated from file foo/bar.proto (syntax proto3)"),
    );
  });

  void test("contains source file info for proto2", async () => {
    const lines = await testGenerate({
      proto: {
        "foo/bar.proto": `syntax="proto2";`,
      },
    });
    assert.ok(
      lines.includes("// @generated from file foo/bar.proto (syntax proto2)"),
    );
  });

  void test("contains source file info for edition 2023", async () => {
    const lines = await testGenerate({
      proto: {
        "foo/bar.proto": `edition="2023";`,
      },
    });
    assert.ok(
      lines.includes("// @generated from file foo/bar.proto (edition 2023)"),
    );
  });

  void test("contains edition file features", async () => {
    const lines = await testGenerate({
      proto: {
        "foo/bar.proto": `
          edition="2023";
          option features.field_presence = EXPLICIT;
          option features.enum_type = OPEN;
        `,
      },
    });
    assert.ok(lines.includes("// option features.field_presence = EXPLICIT;"));
    assert.ok(lines.includes("// option features.enum_type = OPEN;"));
  });

  void test("contains syntax comments", async () => {
    const lines = await testGenerate({
      proto: `
           // comment above...
           // ... the syntax declaration
           syntax="proto3";
           `,
    });
    const firstLines = lines.slice(
      0,
      lines.indexOf("// @generated by test v1"),
    );
    assert.deepStrictEqual(firstLines, [
      "// comment above...",
      "// ... the syntax declaration",
      "",
    ]);
  });

  void test("contains syntax comments with edition 2023", async () => {
    const lines = await testGenerate({
      proto: `
                // comment above...
                // ... the syntax declaration
                edition="2023";
           `,
    });
    const firstLines = lines.slice(
      0,
      lines.indexOf("// @generated by test v1"),
    );
    assert.deepStrictEqual(firstLines, [
      "// comment above...",
      "// ... the syntax declaration",
      "",
    ]);
  });

  void test("contains package comments", async () => {
    const lines = await testGenerate({
      proto: `
                syntax="proto3";
                
                // comment above...
                // ... the package declaration
                package foo;
           `,
    });
    assert.deepStrictEqual(lines.slice(3), [
      "",
      "// comment above...",
      "// ... the package declaration",
      "",
      "const placeholder = 1; // ensure file is not considered empty",
    ]);
  });

  // test helper to generate just a file with a preamble for each input proto file
  async function testGenerate(opt: {
    proto: string | Record<string, string>;
    parameter?: string;
    name?: string;
    version?: string;
  }) {
    return await createTestPluginAndRun({
      ...opt,
      generateAny(f, schema) {
        f.preamble(schema.files[0]);
        f.print(
          "const placeholder = 1; // ensure file is not considered empty",
        );
      },
      parseOptions() {
        // accept all options
        return {};
      },
      returnLinesOfFirstFile: true,
    });
  }
});
